#ifndef GTD_COMMON_HPP
#define GTD_COMMON_HPP

// SDL API
#include <SDL.h>
#include <SDL_opengl.h>

// CUDA C API
#include <cuda_runtime.h>
#include <cuda_gl_interop.h>

// standard libraries
#include <string>
#include <vector>
#include <stdexcept>
#include <memory>
#include <map>
#include <set>
#include <fstream>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <algorithm>

// compile-time options
#ifndef GTD_PROFILING
#define GTD_PROFILING 1
#endif
#ifndef GTD_ERROR_CHECKING
#define GTD_ERROR_CHECKING 1
#endif

// utility macros
#define NAMESPACE_BEG(x) namespace x {
#define NAMESPACE_END(x) }

#define CONCAT_EXPAND(x, y) x##y
#define CONCAT(x, y) CONCAT_EXPAND(x, y)

// utility types
typedef unsigned int uint;

// utility functions
template<class T>
inline std::string toString(const T& value)
{
    std::stringstream ss;
    ss << value;
    return ss.str();
}

// utilitie classes
NAMESPACE_BEG(gtd)

struct FeaturePair
{
    float2 oldPos;
    float2 newPos;
};

class Error: public std::runtime_error
{
public:
    Error(const std::string& msg, const char* file, int line);
};

#define GTD_ERROR(msg) gtd::Error(msg, __FILE__, __LINE__)

#if GTD_ERROR_CHECKING
#define assert(code)                                            \
    if(!(code))                                                 \
        throw gtd::Error("assert(" #code ")", __FILE__, __LINE__)
#else
#define assert(code)
#endif

NAMESPACE_END(gtd)

// SDL extensions
NAMESPACE_BEG(sdl)

class Error: public std::runtime_error
{
public:
    Error(const char* msg, const char* code, const char* file, int line);
};

#if GTD_ERROR_CHECKING
#define SDL_ERROR_CHECK(code)                                   \
    if(code)                                                    \
        throw sdl::Error(SDL_GetError(), #code, __FILE__, __LINE__)
#else
#define SDL_ERROR_CHECK(code) (code)
#endif

NAMESPACE_END(sdl)

// GL extensions
NAMESPACE_BEG(gl)

class Error: public std::runtime_error
{
public:
    Error(GLenum status, const char* code, const char* file, int line);
    const GLenum status;
};

#if GTD_ERROR_CHECKING
#define GL_ERROR_CHECK(code)                                               \
    (code); GLenum CONCAT(status, __LINE__) = glGetError();                 \
    if(CONCAT(status, __LINE__) != GL_NO_ERROR)                             \
        throw gl::Error(CONCAT(status, __LINE__), #code, __FILE__, __LINE__)
#else
#define GL_ERROR_CHECK(code) (code)
#endif

NAMESPACE_END(gl)

// CUDA extensions
NAMESPACE_BEG(cuda)

class Error: public std::runtime_error
{
public:
    Error(cudaError_t status, const char* code, const char* file, int line);
    const cudaError_t status;
};

#if GTD_ERROR_CHECKING
#define CUDA_ERROR_CHECK(code)                                              \
    cudaError_t CONCAT(status, __LINE__) = (code);                          \
    if(CONCAT(status, __LINE__) != cudaSuccess)                             \
        throw cuda::Error(CONCAT(status, __LINE__), #code, __FILE__, __LINE__)
#else
#define CUDA_ERROR_CHECK(code) (code)
#endif

NAMESPACE_END(cuda)

#endif
